/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.sipper.modeling;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

public class CircularGenome<G> extends Genome<G> {

	public CircularGenome(List<G> genesSequence) {
		super(genesSequence);
		// TODO Auto-generated constructor stub
	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public List<G> smallestInducedIntervalOfGenes(Collection<G> genes) {
		List<G> result = new ArrayList<G>();	
		SortedSet<Integer> ranks = new TreeSet<Integer>(this.getRanks(genes));
		if(!ranks.isEmpty())
		{			
			// the search of the smallest genes interval induced by "genes" is realized by searching the biggest gene interval that only contains two genes of "genes".
			LinkedList<Integer> temp = new LinkedList<Integer>(ranks);
			Collections.sort(temp); //TODO needed?
			// gene interval from the highest ranked gene to the smallest (i.e. the interval include the last genes and the first ones).
			Integer endBiggestInterval = temp.getLast();
			Integer beginBiggestInterval = temp.getFirst();
			Integer biggestInterval = this.size() - (endBiggestInterval - beginBiggestInterval) +1;
			Integer leftRank = beginBiggestInterval;
			// the first loop with rightRank = leftRank allows us to test the case were the interval is composed of one gene.
			for (Integer rightRank :  temp)
			{
				Integer biggestIntervalTemp = rightRank - leftRank +1;
				if (biggestIntervalTemp > biggestInterval)
				{
					biggestInterval = biggestIntervalTemp;
					beginBiggestInterval = leftRank;
					endBiggestInterval = rightRank;
				}
				leftRank = rightRank;
			}
			if (endBiggestInterval <= beginBiggestInterval)
			{
				// the biggest gene interval that contains only two genes of "genes" begins at endBiggestInterval and finish at beginBiggestInterval
				// that means the smaller gene interval induced by "genes" goes from beginBiggestInterval to endBiggestInterval
				result = new ArrayList<G>(this.subList(beginBiggestInterval,endBiggestInterval+1));
			}
			else
			{
				result = new ArrayList<G>(this.subList(endBiggestInterval,this.size()));
				result.addAll(this.subList(0, beginBiggestInterval));
			}
		}
		ranks.clear();
		return result;
	}

	@Override
	public double sizeOfSmallestInducedIntervalOGenes(Collection<G> genes) {
		double result = Double.NaN;	
		SortedSet<Integer> ranks = new TreeSet<Integer>(this.getRanks(genes));
		if(!ranks.isEmpty())
		{			
			// the search of the smallest genes interval induced by "genes" is realized by searching the biggest gene interval that only contains two genes of "genes".
			LinkedList<Integer> temp = new LinkedList<Integer>(ranks);
			Collections.sort(temp); //TODO needed?
			// gene interval from the highest ranked gene to the smallest (i.e. the interval include the last genes and the first ones).
			Integer endBiggestInterval = temp.getLast();
			Integer beginBiggestInterval = temp.getFirst();
			Integer biggestInterval = this.size() - (endBiggestInterval - beginBiggestInterval) +1;
			Integer leftRank = beginBiggestInterval;
			// the first loop with rightRank = leftRank allows us to test the case were the interval is composed of one gene.
			for (Integer rightRank :  temp)
			{
				Integer biggestIntervalTemp = rightRank - leftRank +1;
				if (biggestIntervalTemp > biggestInterval)
				{
					biggestInterval = biggestIntervalTemp;
					beginBiggestInterval = leftRank;
					endBiggestInterval = rightRank;
				}
				leftRank = rightRank;
			}
			result = this.size() - biggestInterval +2;
		}	
		ranks.clear();
		return result;
	}
	
}
